/*
 * main.c
 *
 *  Created on: Apr 2, 2010
 *      Author: ren
 */

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>


//monitor RW_Controller {
//    int nr = 0, nw = 0; //(nr == 0 V nw == 0) ^ nw <= 1
//    cond oktoread,      //signaled when nw == 0
//        oktowrite;      //signaled when nr == 0 e nw == 0
//    procedure request_read() {
//        while (nw > 0) wait(oktoread);
//        nr = nr + 1;
//    }
//
//    procedure release_read() {
//        nr = nr - 1;
//        if (nr == 0) signal(oktowrite); //awaken one writer
//    }
//
//    procedure request_write() {
//        while (nr > 0 || nw > 0) wait (oktowrite);
//        nw = nw + 1;
//    }
//
//    procedure release_write() {
//        nw = nw - 1;
//        signal(oktowrite);      //awaken one writer and
//        signal_all(oktoread);   // all readers
//    }
//}

int escritores_juntos = 0;
int leitores_juntos = 0;


int nr = 0, nw = 0; //(nr == 0 V nw == 0) ^ nw <= 1
//cond oktoread,      //signaled when nw == 0
//oktowrite;      //signaled when nr == 0 e nw == 0

sem_t monitor,leituras;
pthread_mutex_t rl = PTHREAD_MUTEX_INITIALIZER,
		wl = PTHREAD_MUTEX_INITIALIZER;

int request_read() {
	if (nw > 0) return 0;
	else {
		nr++;
		return 1;
	}
}

void release_read() {
	nr = nr - 1;
	if (nr == 0)  sem_post(&leituras);
}

int request_write() {
	if (nw > 0 || nr > 0) return 0;
	else {
		nw++;
		return 1;
	}
}

void release_write() {
	nw = nw - 1;
}

void *Escritor(void *threadid)
{
    long tid;


    tid = (long)threadid;

    while (1) {
//    	printf("pedindo pra escrever \n");
    	pthread_mutex_lock(&rl);
    	sem_wait(&monitor);
    	while (request_write() == 0) {
    		sem_post(&monitor);
    		sem_wait(&monitor);
    	}
    	sem_post(&monitor);
    	pthread_mutex_unlock(&rl);
    	escritores_juntos++;
    	printf("escritores %d leitores %d \n", escritores_juntos,leitores_juntos);
    	sleep(rand() % 5);
    	escritores_juntos--;
//    	printf("liberando escrita \n");
    	sem_wait(&monitor);
    	release_write();
    	sem_post(&monitor);
    	sleep(rand() % 5);
    }
}

void *Leitor(void *threadid)
{
    long tid;


    tid = (long)threadid;

    while (1) {
//    	printf("pedindo pra ler \n");
    	pthread_mutex_lock(&rl);
    	sem_wait(&monitor);
    	while (request_read() == 0) {
    		sem_post(&monitor);
			sem_wait(&monitor);
    	}
    	sem_post(&monitor);
    	pthread_mutex_unlock(&rl);
    	leitores_juntos++;
    	printf("escritores %d leitores %d \n", escritores_juntos,leitores_juntos);
    	sleep(rand() % 5);
    	leitores_juntos--;
//    	printf("liberando leitura \n");
    	sem_wait(&monitor);
    	release_read();
    	sem_post(&monitor);
    	sleep(rand() % 5);
    }
}


int main(int argc, char *argv[])
{
	int leitores = atoi(argv[1]);
	int escritores = atoi(argv[2]);
    printf("Leitores-escritores! com %d leitores e %d escritores\n",leitores,escritores);

    pthread_t threads[leitores*escritores];

    pthread_attr_t thread_attr;

    int my_policy = 0;

    pthread_attr_init(&thread_attr);
    pthread_attr_getschedpolicy(&thread_attr,&my_policy);
    pthread_attr_setschedpolicy(&thread_attr, SCHED_FIFO);

    sem_init(&monitor,0,1);
    sem_init(&leituras,0,1);

    int rc;
    long t;

    for (t=0; t<leitores; t++)
    {
        printf("In main: creating thread %ld\n", t);
        rc = pthread_create(&threads[t], NULL, Leitor, (void *)t);

        if (rc)
        {
            printf("ERROR; return code from pthread_create() is %d\n", rc);
            exit(-1);
        }
    }

    for (t=0; t<escritores; t++)
    {
        printf("In main: creating thread %ld\n", t);
        rc = pthread_create(&threads[t], NULL, Escritor, (void *)t);

        if (rc)
        {
            printf("ERROR; return code from pthread_create() is %d\n", rc);
            exit(-1);
        }
    }
    pthread_exit(NULL);

    sem_destroy(&monitor);

    return 0;
}
